<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>STOMP 多连接演示</title>
    <script src="https://cdn.jsdelivr.net/npm/stompjs@2.3.3/lib/stomp.min.js"></script>
    <style>
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            margin: 0;
            padding: 20px;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
        }
        .container {
            max-width: 1400px;
            margin: 0 auto;
            background: white;
            border-radius: 10px;
            box-shadow: 0 10px 30px rgba(0,0,0,0.2);
            overflow: hidden;
        }
        .header {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            padding: 20px;
            text-align: center;
        }
        .controls {
            background: #f8f9fa;
            padding: 20px;
            border-bottom: 1px solid #dee2e6;
            display: flex;
            gap: 15px;
            align-items: center;
            flex-wrap: wrap;
        }
        .controls button {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            border: none;
            padding: 10px 20px;
            border-radius: 5px;
            cursor: pointer;
            font-size: 14px;
            transition: transform 0.2s;
        }
        .controls button:hover {
            transform: translateY(-1px);
        }
        .controls button:disabled {
            background: #6c757d;
            cursor: not-allowed;
            transform: none;
        }
        .connections {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
            gap: 20px;
            padding: 20px;
        }
        .connection {
            background: #f8f9fa;
            border-radius: 8px;
            border: 2px solid #e9ecef;
            overflow: hidden;
        }
        .connection.active {
            border-color: #28a745;
        }
        .connection-header {
            background: #e9ecef;
            padding: 15px;
            display: flex;
            justify-content: space-between;
            align-items: center;
            border-bottom: 1px solid #dee2e6;
        }
        .connection-info {
            display: flex;
            flex-direction: column;
            gap: 5px;
        }
        .connection-id {
            font-weight: bold;
            color: #495057;
        }
        .connection-status {
            font-size: 12px;
            padding: 2px 8px;
            border-radius: 10px;
            color: white;
        }
        .connection-status.connected {
            background: #28a745;
        }
        .connection-status.disconnected {
            background: #dc3545;
        }
        .connection-controls {
            display: flex;
            gap: 8px;
        }
        .connection-controls button {
            background: #6c757d;
            color: white;
            border: none;
            padding: 6px 12px;
            border-radius: 4px;
            cursor: pointer;
            font-size: 12px;
        }
        .connection-controls button:hover {
            background: #5a6268;
        }
        .connection-content {
            padding: 20px;
        }
        .message-inputs {
            display: grid;
            gap: 15px;
            margin-bottom: 20px;
        }
        .form-group {
            display: flex;
            flex-direction: column;
            gap: 5px;
        }
        .form-group label {
            font-weight: bold;
            color: #495057;
            font-size: 14px;
        }
        .form-group input {
            padding: 8px 12px;
            border: 1px solid #ced4da;
            border-radius: 4px;
            font-size: 14px;
        }
        .action-buttons {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 20px;
            margin-bottom: 20px;
        }
        .subscription-section, .sending-section {
            background: #f8f9fa;
            padding: 15px;
            border-radius: 6px;
            border: 1px solid #dee2e6;
        }
        .subscription-section h4, .sending-section h4 {
            margin: 0 0 10px 0;
            color: #495057;
            font-size: 14px;
        }
        .subscription-buttons, .message-buttons {
            display: flex;
            flex-direction: column;
            gap: 8px;
        }
        .subscription-buttons button, .message-buttons button {
            background: #6c757d;
            color: white;
            border: none;
            padding: 8px 12px;
            border-radius: 4px;
            cursor: pointer;
            font-size: 12px;
            transition: background-color 0.2s;
        }
        .subscription-buttons button:hover, .message-buttons button:hover {
            background: #5a6268;
        }
        .send-btn {
            background: #007bff !important;
        }
        .send-btn:hover {
            background: #0056b3 !important;
        }
        .broadcast-btn {
            background: #28a745 !important;
        }
        .broadcast-btn:hover {
            background: #1e7e34 !important;
        }
        .room-btn {
            background: #007bff !important;
        }
        .room-btn:hover {
            background: #0056b3 !important;
        }
        .chat-btn {
            background: #fd7e14 !important;
        }
        .chat-btn:hover {
            background: #e8690b !important;
        }
        .clear-btn {
            background: #dc3545;
            color: white;
            border: none;
            padding: 8px 16px;
            border-radius: 4px;
            cursor: pointer;
            font-size: 12px;
            width: 100%;
            margin-top: 10px;
            transition: background-color 0.2s;
        }
        .clear-btn:hover {
            background: #c82333;
        }
        .messages {
            height: 300px;
            overflow-y: auto;
            background: white;
            border: 1px solid #ced4da;
            border-radius: 4px;
            padding: 10px;
            font-family: monospace;
            font-size: 11px;
        }
        .message {
            margin-bottom: 5px;
            padding: 3px;
            border-radius: 3px;
            font-size: 11px;
        }
        .message.broadcast { background: #e3f2fd; }
        .message.room { background: #f3e5f5; }
        .message.chat { background: #e8f5e8; }
        .message.system { background: #f5f5f5; }
        .message.error { background: #ffebee; color: #c62828; }
        .connection-count {
            color: #495057;
            font-size: 14px;
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="header">
            <h1>STOMP 多连接演示</h1>
            <p>每个连接代表一个用户，支持广播、房间、私聊三种消息类型</p>
        </div>
        
        <!-- 控制面板 -->
        <div class="controls">
            <button onclick="addConnection()">添加新连接</button>
            <button onclick="removeAllConnections()">删除所有连接</button>
            <span class="connection-count">当前连接数: <span id="connectionCount">0</span></span>
        </div>
        
        <!-- 连接列表 -->
        <div class="connections" id="connections">
            <!-- 连接将在这里动态生成 -->
        </div>
    </div>

    <script>
        let connectionCounter = 0;
        let connections = new Map();

        // 连接类
        class Connection {
            constructor(id, userId) {
                this.id = id;
                this.userId = userId;
                this.stompClient = null;
                this.subscriptions = new Map();
                this.connected = false;
                this.roomId = 'room1';
                this.targetUserId = 'user' + (parseInt(userId.replace('user', '')) + 1);
            }

            connect() {
                const socket = new WebSocket('ws://' + window.location.host + '/ws-stomp');
                this.stompClient = Stomp.over(socket);
                
                this.stompClient.connect({}, (frame) => {
                    this.connected = true;
                    this.updateStatus();我
                    this.addMessage('系统', '连接成功: ' + frame, 'system');
                }, (error) => {
                    this.connected = false;
                    this.updateStatus();
                    this.addMessage('系统', '连接失败: ' + error, 'error');
                });
            }

            disconnect() {
                if (this.stompClient !== null) {
                    this.stompClient.disconnect(() => {
                        this.connected = false;
                        this.updateStatus();
                        this.addMessage('系统', '连接已断开', 'system');
                        this.subscriptions.clear();
                    });
                }
            }

            updateStatus() {
                const statusElement = document.querySelector(`#connection-${this.id} .connection-status`);
                if (statusElement) {
                    statusElement.textContent = this.connected ? '已连接' : '未连接';
                    statusElement.className = `connection-status ${this.connected ? 'connected' : 'disconnected'}`;
                }
            }

            addMessage(type, content, category = 'system') {
                const messagesElement = document.querySelector(`#connection-${this.id} .messages`);
                if (messagesElement) {
                    const messageDiv = document.createElement('div');
                    messageDiv.className = `message ${category}`;
                    messageDiv.innerHTML = `<strong>[${new Date().toLocaleTimeString()}] ${type}:</strong> ${content}`;
                    messagesElement.appendChild(messageDiv);
                    messagesElement.scrollTop = messagesElement.scrollHeight;
                }
            }

            // 发送广播消息
            sendBroadcast() {
                const content = document.querySelector(`#connection-${this.id} #broadcast-${this.id}`).value;
                if (!content) return;
                
                const message = {
                    content: content,
                    userId: this.userId,
                    timestamp: Date.now()
                };
                
                this.stompClient.send("/app/broadcast", {}, JSON.stringify(message));
                this.addMessage('发送', `广播消息: ${content}`, 'broadcast');
            }

            // 发送房间消息
            sendRoomMessage() {
                const content = document.querySelector(`#connection-${this.id} #room-${this.id}`).value;
                if (!content) return;
                
                // 获取当前房间ID
                const currentRoomId = document.querySelector(`#connection-${this.id} #roomId-${this.id}`).value || 'room1';
                
                const message = {
                    content: content,
                    userId: this.userId,
                    roomId: currentRoomId,
                    timestamp: Date.now()
                };
                
                this.stompClient.send(`/app/room/${currentRoomId}/send`, {}, JSON.stringify(message));
                this.addMessage('发送', `房间消息: ${content}`, 'room');
            }

            // 发送私聊消息
            sendChatMessage() {
                const content = document.querySelector(`#connection-${this.id} #chat-${this.id}`).value;
                if (!content) return;
                
                // 获取用户输入的目标用户ID
                const targetUserId = document.querySelector(`#connection-${this.id} #targetUserId-${this.id}`).value;
                if (!targetUserId) {
                    this.addMessage('错误', '请输入目标用户ID', 'error');
                    return;
                }
                
                const message = {
                    content: content,
                    userId: this.userId,
                    timestamp: Date.now()
                };
                
                const headers = {
                    'user-id': this.userId
                };
                
                this.stompClient.send(`/app/chat/${targetUserId}/send`, headers, JSON.stringify(message));
                this.addMessage('发送', `私聊给用户 ${targetUserId}: ${content}`, 'chat');
            }

            // 订阅广播
            subscribeToBroadcast() {
                // 检查是否已经订阅
                if (this.subscriptions.has('broadcast')) {
                    this.addMessage('系统', '已经订阅了广播消息，无需重复订阅', 'system');
                    return;
                }
                
                const subscription = this.stompClient.subscribe('/broadcast', (message) => {
                    const body = JSON.parse(message.body);
                    this.addMessage('广播', `${body.userId}: ${body.content}`, 'broadcast');
                });
                this.subscriptions.set('broadcast', subscription);
                this.addMessage('订阅', '已订阅广播消息 /broadcast', 'system');
            }

            // 订阅房间
            subscribeToRoom() {
                // 获取当前房间ID
                const currentRoomId = document.querySelector(`#connection-${this.id} #roomId-${this.id}`).value || 'room1';
                
                // 检查是否已经订阅该房间
                if (this.subscriptions.has(`room-${currentRoomId}`)) {
                    this.addMessage('系统', `已经订阅了房间 ${currentRoomId}，无需重复订阅`, 'system');
                    return;
                }
                
                // 取消其他房间的订阅
                this.subscriptions.forEach((subscription, key) => {
                    if (key.startsWith('room-') && key !== `room-${currentRoomId}`) {
                        subscription.unsubscribe();
                        this.subscriptions.delete(key);
                        this.addMessage('系统', `已取消订阅房间: ${key.replace('room-', '')}`, 'system');
                    }
                });
                
                const subscription = this.stompClient.subscribe(`/room/${currentRoomId}`, (message) => {
                    const body = JSON.parse(message.body);
                    this.addMessage('房间', `${body.userId}: ${body.content}`, 'room');
                });
                this.subscriptions.set(`room-${currentRoomId}`, subscription);
                this.addMessage('订阅', `已订阅房间 /room/${currentRoomId}`, 'system');
            }

            // 订阅私聊
            subscribeToChat() {
                // 检查是否已经订阅私聊
                if (this.subscriptions.has('chat')) {
                    this.addMessage('系统', '已经订阅了私聊消息，无需重复订阅', 'system');
                    return;
                }
                
                const subscription = this.stompClient.subscribe(`/chat/${this.userId}`, (message) => {
                    const body = JSON.parse(message.body);
                    this.addMessage('私聊', `收到来自 ${body.userId} 的私聊: ${body.content}`, 'chat');
                });
                this.subscriptions.set('chat', subscription);
                this.addMessage('订阅', `已订阅私聊消息 /chat/${this.userId}`, 'system');
            }

            // 取消所有订阅
            unsubscribeAll() {
                this.subscriptions.forEach((subscription, key) => {
                    subscription.unsubscribe();
                    this.addMessage('取消订阅', `已取消订阅: ${key}`, 'system');
                });
                this.subscriptions.clear();
            }

            // 清空消息
            clearMessages() {
                const messagesElement = document.querySelector(`#connection-${this.id} .messages`);
                if (messagesElement) {
                    messagesElement.innerHTML = '';
                }
            }
        }

        // 添加新连接
        function addConnection() {
            connectionCounter++;
            const userId = `user${connectionCounter}`;
            const connection = new Connection(connectionCounter, userId);
            
            connections.set(connectionCounter, connection);
            
            // 创建连接UI
            const connectionHtml = `
                <div class="connection" id="connection-${connectionCounter}">
                    <div class="connection-header">
                        <div class="connection-info">
                            <div class="connection-id">连接 ${connectionCounter}</div>
                            <div class="connection-status disconnected">未连接</div>
                        </div>
                        <div class="connection-controls">
                            <button onclick="connectConnection(${connectionCounter})">连接</button>
                            <button onclick="disconnectConnection(${connectionCounter})">断开</button>
                            <button onclick="removeConnection(${connectionCounter})">删除</button>
                        </div>
                    </div>
                    <div class="connection-content">
                        <div class="message-inputs">
                            <div class="form-group">
                                <label>广播消息:</label>
                                <input type="text" id="broadcast-${connectionCounter}" placeholder="输入广播消息">
                            </div>
                            <div class="form-group">
                                <label>房间ID:</label>
                                <input type="text" id="roomId-${connectionCounter}" placeholder="输入房间ID" value="room1">
                            </div>
                            <div class="form-group">
                                <label>房间消息:</label>
                                <input type="text" id="room-${connectionCounter}" placeholder="输入房间消息">
                            </div>
                            <div class="form-group">
                                <label>私聊消息:</label>
                                <input type="text" id="chat-${connectionCounter}" placeholder="输入私聊消息">
                            </div>
                            <div class="form-group">
                                <label>目标用户ID:</label>
                                <input type="text" id="targetUserId-${connectionCounter}" placeholder="输入目标用户ID" value="user2">
                            </div>
                        </div>
                        
                        <div class="messages"></div>
                        
                        <div class="action-buttons">
                            <div class="subscription-section">
                                <h4>订阅管理</h4>
                                <div class="subscription-buttons">
                                    <button onclick="subscribeToBroadcast(${connectionCounter})">订阅广播</button>
                                    <button onclick="subscribeToRoom(${connectionCounter})">订阅房间</button>
                                    <button onclick="subscribeToChat(${connectionCounter})">订阅私聊</button>
                                    <button onclick="unsubscribeAll(${connectionCounter})">取消订阅</button>
                                </div>
                            </div>
                            
                            <div class="sending-section">
                                <h4>消息发送</h4>
                                <div class="message-buttons">
                                    <button onclick="sendBroadcast(${connectionCounter})" class="send-btn broadcast-btn">发送广播</button>
                                    <button onclick="sendRoomMessage(${connectionCounter})" class="send-btn room-btn">发送房间消息</button>
                                    <button onclick="sendChatMessage(${connectionCounter})" class="send-btn chat-btn">发送私聊</button>
                                </div>
                            </div>
                        </div>
                        
                        <button onclick="clearMessages(${connectionCounter})" class="clear-btn">清空消息</button>
                    </div>
                </div>
            `;
            
            document.getElementById('connections').insertAdjacentHTML('beforeend', connectionHtml);
            updateConnectionCount();
        }

        // 连接管理函数
        function connectConnection(id) {
            const connection = connections.get(id);
            if (connection) {
                connection.connect();
            }
        }

        function disconnectConnection(id) {
            const connection = connections.get(id);
            if (connection) {
                connection.disconnect();
            }
        }

        function removeConnection(id) {
            const connection = connections.get(id);
            if (connection) {
                connection.disconnect();
                connections.delete(id);
                document.getElementById(`connection-${id}`).remove();
                updateConnectionCount();
            }
        }

        function removeAllConnections() {
            connections.forEach((connection, id) => {
                connection.disconnect();
            });
            connections.clear();
            document.getElementById('connections').innerHTML = '';
            updateConnectionCount();
        }

        // 消息发送函数
        function sendBroadcast(id) {
            const connection = connections.get(id);
            if (connection) {
                connection.sendBroadcast();
            }
        }

        function sendRoomMessage(id) {
            const connection = connections.get(id);
            if (connection) {
                connection.sendRoomMessage();
            }
        }

        function sendChatMessage(id) {
            const connection = connections.get(id);
            if (connection) {
                connection.sendChatMessage();
            }
        }

        // 订阅函数
        function subscribeToBroadcast(id) {
            const connection = connections.get(id);
            if (connection && connection.connected) {
                connection.subscribeToBroadcast();
            }
        }

        function subscribeToRoom(id) {
            const connection = connections.get(id);
            if (connection && connection.connected) {
                connection.subscribeToRoom();
            }
        }

        function subscribeToChat(id) {
            const connection = connections.get(id);
            if (connection && connection.connected) {
                connection.subscribeToChat();
            }
        }

        function unsubscribeAll(id) {
            const connection = connections.get(id);
            if (connection) {
                connection.unsubscribeAll();
            }
        }

        function clearMessages(id) {
            const connection = connections.get(id);
            if (connection) {
                connection.clearMessages();
            }
        }

        function updateConnectionCount() {
            document.getElementById('connectionCount').textContent = connections.size;
        }

        // 页面加载时自动添加第一个连接
        window.onload = function() {
            addConnection();
        };
    </script>

    <!-- STOMP功能特性演示说明 -->
    <div style="background-color: #e8f4fd; padding: 15px; border-radius: 5px; margin-top: 20px; border-left: 4px solid #2196F3;">
        <h4 style="margin-top: 0; color: #1976D2;">STOMP 协议功能特性演示</h4>
        <p><strong>功能说明：</strong>这是一个基于STOMP协议的实时通信演示系统，展示了STOMP相比纯WebSocket的简化特性和强大功能。</p>
        
        <p><strong>全局控制功能：</strong></p>
        <ul style="margin: 8px 0;">
            <li><strong>添加新连接：</strong>创建新的STOMP连接（每个连接代表一个用户）</li>
            <li><strong>删除所有连接：</strong>同时关闭所有活跃连接并清理资源</li>
            <li><strong>连接计数：</strong>实时显示当前活跃连接数量</li>
        </ul>
        
        <p><strong>连接管理功能：</strong></p>
        <ul style="margin: 8px 0;">
            <li><strong>连接：</strong>建立WebSocket连接并升级为STOMP协议</li>
            <li><strong>断开：</strong>断开STOMP连接但保留连接面板</li>
            <li><strong>删除：</strong>彻底删除连接面板和相关数据</li>
        </ul>
        
        <p><strong>STOMP消息类型演示：</strong></p>
        <ul style="margin: 8px 0;">
            <li><strong>广播消息：</strong>发送到所有订阅者的全局消息（绿色标识）</li>
            <li><strong>房间消息：</strong>发送到特定房间的群组消息（蓝色标识）</li>
            <li><strong>私聊消息：</strong>点对点的私人聊天消息（紫色标识）</li>
        </ul>
        
        <p><strong>STOMP订阅管理：</strong></p>
        <ul style="margin: 8px 0;">
            <li><strong>订阅广播：</strong>接收所有广播消息</li>
            <li><strong>订阅房间：</strong>接收特定房间的消息（可动态切换房间）</li>
            <li><strong>订阅私聊：</strong>接收发送给自己的私聊消息</li>
            <li><strong>取消所有订阅：</strong>一次性取消所有消息订阅</li>
        </ul>
        
        <p><strong>STOMP协议特性：</strong></p>
        <ul style="margin: 8px 0;">
            <li><strong>内置路由：</strong>消息自动路由到正确的目的地，无需手动管理连接池</li>
            <li><strong>目的地管理：</strong>支持动态订阅/取消订阅，自动处理消息分发</li>
            <li><strong>消息头支持：</strong>支持自定义消息头（如user-id用于私聊认证）</li>
            <li><strong>连接状态管理：</strong>自动处理连接建立、断开和重连</li>
        </ul>
        
        <p><strong>消息显示特点：</strong></p>
        <ul style="margin: 8px 0;">
            <li>每条消息都显示时间戳和连接ID，不同连接用不同颜色标识</li>
            <li>区分消息类型：广播消息（绿色）、房间消息（蓝色）、私聊消息（紫色）</li>
            <li>系统消息：连接状态、订阅状态、错误信息等</li>
            <li>消息来源：显示消息的发送者和接收者信息</li>
        </ul>
        
        <p><strong>建议测试流程：</strong></p>
        <ol style="margin: 8px 0;">
            <li>创建2-3个连接，观察STOMP连接建立过程</li>
            <li>测试广播功能：一个连接发送广播，所有连接订阅并接收</li>
            <li>测试房间功能：设置不同房间ID，观察消息隔离效果</li>
            <li>测试私聊功能：使用不同用户ID发送私聊消息</li>
            <li>测试订阅管理：动态订阅/取消订阅不同类型的消息</li>
            <li>测试连接管理：断开重连，观察订阅状态保持</li>
            <li>观察STOMP协议：在浏览器开发者工具中查看WebSocket帧</li>
        </ol>
        
        <p><strong>STOMP vs 纯WebSocket对比：</strong></p>
        <ul style="margin: 8px 0;">
            <li><strong>代码简化：</strong>广播消息只需一行代码 vs 手动遍历连接池</li>
            <li><strong>路由自动化：</strong>消息自动路由 vs 手动实现消息分发逻辑</li>
            <li><strong>订阅管理：</strong>内置订阅机制 vs 手动管理用户-连接映射</li>
            <li><strong>协议标准化：</strong>标准STOMP协议 vs 自定义消息格式</li>
        </ul>
        
        <p><strong>技术亮点：</strong></p>
        <ul style="margin: 8px 0;">
            <li>基于Spring Boot的STOMP消息代理配置</li>
            <li>使用@MessageMapping和@SendTo注解简化消息处理</li>
            <li>支持动态目的地订阅和消息路由</li>
            <li>完整的STOMP客户端实现（使用stompjs库）</li>
            <li>响应式UI设计，支持多连接并发测试</li>
        </ul>
    </div>
</body>
</html>


